﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using WorkStation.Frameworks.Core;
using WorkStation.Frameworks.Core.Extensions;
using WorkStation.Frameworks.EasyBus.DbModel;
using WorkStation.Frameworks.EasyBus.Extensions;
using WorkStation.Frameworks.EasyBus.Attributes;

namespace WorkStation.Frameworks.EasyBus
{
    /// <summary>
    /// 配置提供接口
    /// </summary>
    public interface IConfigProvider
    {
        IBus Bus { get; set; }

        /// <summary>
        /// 获取发布信息
        /// </summary>
        /// <returns></returns>
        IEnumerable<PublishInfo> GetPublishInfo();

        /// <summary>
        /// 获取订阅者信息
        /// </summary>
        /// <returns></returns>
        IEnumerable<SubscribeInfo> GetSubscribeInfo();

        /// <summary>
        /// 获取监听者信息
        /// </summary>
        /// <returns></returns>
        IEnumerable<ListenerInfo> GetListenerInfo();
    }

    public class AttributeConfigProvider : IConfigProvider
    {
        public string[] AssemblyNames { get; set; }

        public IBus Bus { get; set; }

        public AttributeConfigProvider(params string[] assemblyNames) 
        {
            if (assemblyNames == null || assemblyNames.Length == 0) 
            {
                throw new ArgumentNullException("assemblyNames", "请输入标记所在程序集全称");
            }

            this.AssemblyNames = assemblyNames;
        }

        public IEnumerable<PublishInfo> GetPublishInfo()
        {
            return this.AssemblyNames.SelectMany(v => Assembly.Load(v).GetTypes()
                                                                      .Select(type => type.GetPublishAttribute())
                                                                      .Where(attribute => attribute != null)
                                     .Select(attribute => new PublishInfo()
                                     {
                                         BusName = this.Bus.Name,
                                         NodeName = this.Bus.NodeName,
                                         Description = attribute.Description,
                                         RouteKey = attribute.RouteKey,
                                         ServerIp = (this.Bus as IBusDataManager).Ip
                                     }));
        }

        public IEnumerable<SubscribeInfo> GetSubscribeInfo()
        {
            return this.AssemblyNames.SelectMany(v => Assembly.Load(v).GetTypes()
                                                                      .SelectMany(type => type.GetSubscribeAttribute())
                                                                      .Where(attribute => attribute != null)
                                     .Select(attribute => new SubscribeInfo()
                                     {
                                         Name = attribute.Name.IsNullOrEmpty() ? Guid.NewGuid().ToString() : attribute.Name,
                                         BusName = this.Bus.Name,
                                         Description = attribute.Description,
                                         Mq = attribute.GetMq(this.Bus),
                                         RouteKey = attribute.RouteKey,
                                         NodeName = this.Bus.NodeName,
                                         PubBusName = attribute.BusName,
                                         SubServerIp = (this.Bus as IBusDataManager).Ip
                                     }));
        }

        public IEnumerable<ListenerInfo> GetListenerInfo()
        {
            IEnumerable<SubscribeInfo> subs = this.GetSubscribeInfo();

            IEnumerable<ListenerAttribute> attributes = this.AssemblyNames.SelectMany(v => Assembly.Load(v).GetTypes()
                                                                          .SelectMany(type => type.GetListenerAttribute())
                                                                          .Where(attribute => attribute != null));

            ListenerAttribute[] error = attributes.Where(v => !subs.Any(s => s.Name.Equals(v.SubscribeName))).ToArray();

            if (error.Length != 0) 
            {
                throw new Exception("监听器指定的订阅名称不存在，{0}".Fmt(string.Join("-", error.Select(v => v.SubscribeName).ToArray())));
            }

            return attributes.Select(attribute => new ListenerInfo()
                                     {
                                         Name = "{0}-Listener".Fmt(attribute.SubscribeName),
                                         Mq = subs.FirstOrDefault(v => v.Name.Equals(attribute.SubscribeName)).Mq,
                                         ProcessThreadCount = attribute.ProcessThreadCount,
                                         NodeName = this.Bus.NodeName,
                                         SubBusName = this.Bus.Name,
                                         Process = new ProcessInfo() { RouteKey = subs.FirstOrDefault(v => v.Name.Equals(attribute.SubscribeName)).RouteKey, HandleName = attribute.GetHandleName() }
                                     });
        }
    }
}
